package com.ipan.kits.mapper;

import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;

/**
 * Json工具
 * 
 * 针对Fastjson的实现，下载https://github.com/alibaba/fastjson/releases；
 * Fastjson对android版本特别做了支持，目的是减去一些android虚拟机dalvik不支持的功能，使得jar文件更小，同时针对dalvik做了很多性能优化，
 * 包括减少方法调用等。parse为JSONObject/JSONArray时比原生org.json速度快，序列化反序列化JavaBean性能比jackson/gson性能更好。
 * 
 * 默认方法FastJsonMapper.me()返回的是JsonMapper支持循环引用的禁用、Date格式使用默认的格式（yyyy-MM-dd HH:mm:ss）转换；
 * 若默认的Date格式不够用，可以自己使用注解@JSONField在Bean上指定格式或者调用toJsonWithDateFormat()方法；
 * 
 * @author iPan
 * @version 2019-06-03
 */
public class FastJsonMapper extends JsonMapper {
	
	private SerializerFeature[] defaultSerializerFeature = null;
	private static final FastJsonMapper _MAPPER = FastJsonMapper.create();
	
	private FastJsonMapper(SerializerFeature[] defaultSerializerFeature) {
		this.defaultSerializerFeature = defaultSerializerFeature;
	}
	
	/**
	 * 默认调用
	 */
	public static FastJsonMapper me() {
		return _MAPPER;
	}
	
	/**
	 * 创建FastJsonMapper
	 * @param disableCircularReference 是否禁用循环引用（true 禁用循环引用，false 默认是不禁用的）
	 * @return FastJsonMapper
	 */
	public static FastJsonMapper create(boolean disableCircularReference) {
		SerializerFeature[] feature = (disableCircularReference) 
				? new SerializerFeature[] {SerializerFeature.DisableCircularReferenceDetect, SerializerFeature.WriteDateUseDateFormat} 
				: new SerializerFeature[] {SerializerFeature.WriteDateUseDateFormat};
		return new FastJsonMapper(feature);
	}
	public static FastJsonMapper create(SerializerFeature[] defaultSerializerFeature) {
		return new FastJsonMapper(defaultSerializerFeature);
	}
	public static FastJsonMapper create() {
		return create(true);
	}
	
	/**
	 * Fastjson默认不会输出null值的字段，该方法是创建支持输出null字段的FastJsonMapper；
	 */
	public static FastJsonMapper createWriteNullMapper(boolean disableCircularReference) {
		SerializerFeature[] defaultSerializerFeature = (disableCircularReference) 
			? new SerializerFeature[] {
				SerializerFeature.DisableCircularReferenceDetect,
				SerializerFeature.WriteMapNullValue,
				SerializerFeature.WriteNullNumberAsZero,
				SerializerFeature.WriteNullListAsEmpty,
				SerializerFeature.WriteNullStringAsEmpty,
				SerializerFeature.WriteNullBooleanAsFalse,
				SerializerFeature.WriteDateUseDateFormat // 默认是yyyy-MM-dd HH:mm:ss，可以使用JSONField去覆盖；
			} 
			: new SerializerFeature[] {
				SerializerFeature.WriteMapNullValue,
				SerializerFeature.WriteNullNumberAsZero,
				SerializerFeature.WriteNullListAsEmpty,
				SerializerFeature.WriteNullStringAsEmpty,
				SerializerFeature.WriteNullBooleanAsFalse,
				SerializerFeature.WriteDateUseDateFormat
			};
		return new FastJsonMapper(defaultSerializerFeature);
	}
	public static FastJsonMapper createWriteNullMapper() {
		return createWriteNullMapper(true);
	}
	
	@Override
	public String toJson(Object object) {
		Validate.notNull(object, "object is null");
		return (defaultSerializerFeature == null) ? JSON.toJSONString(object) : JSON.toJSONString(object, defaultSerializerFeature);
	}
	public String toJsonWithDateFormat(Object object, String dateFormat) {
		Validate.notNull(object, "object is null");
		return (defaultSerializerFeature == null)
				? JSON.toJSONStringWithDateFormat(object, dateFormat, null)
				: JSON.toJSONStringWithDateFormat(object, dateFormat, defaultSerializerFeature);
	}
	
	@Override
	public String toJsonP(String functionName, Object object) {
		Validate.notBlank(functionName, "functionName is empty");
		Validate.notNull(object, "object is null");
		String json = toJson(object);
		return functionName + "(" + json + ")";	}
	
	@Override
	public <T> T fromJson(String jsonString, Class<T> clazz) {
		if (StringUtils.isEmpty(jsonString)) {
			return null;
		}
		return JSON.parseObject(jsonString, clazz);
	}

	@Override
	public Map<String, Object> jsonToMap(String jsonString) {
		if (StringUtils.isEmpty(jsonString)) {
			return null;
		}
		Map<String, Object> map = (Map<String, Object>) JSON.parseObject(jsonString); // JSONObject是Map<String, Object>的实现类；
		return map;
	}

	@Override
	public List<Map<String, Object>> jsonToList(String jsonString) {
		if (StringUtils.isEmpty(jsonString)) {
			return null;
		}
		List<Map<String, Object>> list = (List<Map<String, Object>>) JSON.parse(jsonString); // 返回JSONArray<JSONObject>，JSONArray是List<Object>的实现类；
		return list;
	}

	//-- Fastjson特性方法 --//
	/**
	 * 定制序列化接口(使用SerializeFilter方式)
	 * 
	 * {@link com.alibaba.fastjson.serializer.SerializerFeature}对象枚举
     * <ul>
     * <li>{@link com.alibaba.fastjson.serializer.SerializerFeature#DisableCircularReferenceDetect} 禁用循环引用</li>
     * <li>{@link com.alibaba.fastjson.serializer.SerializerFeature#WriteClassName} json序列化时将类信息保存以便反序列化时可以自动获取类信息</li>
     * <li>{@link com.alibaba.fastjson.serializer.SerializerFeature#BeanToArray} 将Java对象转换为数组，这可能已经不是json序列化的范围之内</li>
     * <li>{@link com.alibaba.fastjson.serializer.SerializerFeature#PrettyFormat} 用于显示格式化过的json字符串，一般用于输出控制台效果较好。</li>
     * <li>{@link com.alibaba.fastjson.serializer.SerializerFeature#UseSingleQuotes} 使用单引号，默认使用双引号</li>
     * <li>{@link com.alibaba.fastjson.serializer.SerializerFeature#WriteDateUseDateFormat} 日期格式化输出，日期格式为：yyyy-MM-dd HH:mm:ss</li>
     * <li>{@link com.alibaba.fastjson.serializer.SerializerFeature#BrowserCompatible} 解决中文乱码，会转成Unicode码</li>
     * </ul>
	 * 
	 * <pre>
	 * Fastjson支持多种方式定制序列化：
	 * 		通过@JSONField定制序列化
	 * 		通过@JSONType定制序列化
	 * 		通过SerializeFilter定制序列化
	 * 		通过ParseProcess定制反序列化
	 * </pre>
	 */
	public static String toJson(Object object, SerializerFeature... features) {
		Validate.notNull(object, "object is null");
        return JSON.toJSONString(object, features);
    }
	public static String toJson(Object object, String dateFormat, SerializerFeature... features) {
		Validate.notNull(object, "object is null");
		Validate.notBlank(dateFormat, "dateFormat is empty");
        return JSON.toJSONStringWithDateFormat(object, dateFormat, features);
    }
	
}
